// Copyright 2013 Petar Maymounkov
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package shutter provides a cross-worker resource hierarchy that encloses local resources shared by a locus
package shutter

import (
	"bytes"
	"fmt"
	"log"
	"sync"

	"circuit/kit/kinfolk"
	"circuit/kit/rh"
	"circuit/kit/rh/ns"
	"circuit/kit/rh/ripple"
)

// Shutter is an ns.Dir which encloses the resource hierarchies exported by a locus.
type Shutter struct {
	kin kinfolk.KinXID
	//
	z struct {
		sync.Mutex
		rsc map[string]rh.FID // cross-FID
		fs  rh.FID
	}
	helpFID rh.FID
	langFID rh.FID
	dbgFID  rh.FID
}

// R is a pair of a resource share name and a source
type R struct {
	Name   string
	Source interface{} // rh.Server, rh.FID, ns.Dir
}

// New creates a non-cross RH server object for a shutter namespace
func New(kinXID kinfolk.KinXID, rr ...R) (*Shutter, rh.Server, error) {
	slash, err := NewShutter(kinXID, rr...)
	if err != nil {
		return nil, nil, err
	}
	return slash, ns.New(slash), nil
}

func NewShutter(kinXID kinfolk.KinXID, rr ...R) (*Shutter, error) {
	x := &Shutter{kin: kinXID}
	x.z.rsc = make(map[string]rh.FID)
	x.helpFID = ripple.NewHelp(buildShutterHelp(kinXID.ID.String(), kinXID.X.Addr().String()))
	_, x.langFID = ripple.NewSlab("/")
	x.dbgFID = NewDbg()
	//
	var w bytes.Buffer
	fmt.Fprintf(&w, "shutter cross-hierarchies: ")
	for _, r := range rr {
		var fid rh.FID
		switch t := r.Source.(type) {
		case rh.Server:
			ssn, err := t.SignIn("shutter", "/")
			if err != nil {
				return nil, err
			}
			fid, err = ssn.Walk(nil)
			if err != nil {
				return nil, err
			}
		case rh.FID:
			fid = t
		case ns.Dir:
			fid = ns.NewFID(r.Name, t)
		default:
			panic("x")
		}
		x.z.rsc[r.Name] = fid
		if r.Name == "fs" {
			x.z.fs = fid
		}
		fmt.Fprintf(&w, "%s, ", r.Name)
	}
	log.Println(w.String())
	return x, nil
}

func (x *Shutter) Fs() rh.FID {
	x.z.Lock()
	defer x.z.Unlock()
	return x.z.fs
}

func (x *Shutter) Lang() rh.FID {
	return x.langFID
}

func (x *Shutter) Help() rh.FID {
	return x.helpFID
}

func (x *Shutter) Dbg() rh.FID {
	return x.dbgFID
}

const shutterHelpFmt = `
  This is the home directory of the circuit locus %s,
  located at address %s.

  SYNOPSIS

  cd lang    # Directory lang encloses the live programming environment at the locus.
  cd fs      # Directory fs mirrors the local file system at the locus.
  cat help   # Read this help screen.
  cd dbg     # Directory dbg provides access to runtime stats of the circuit process.

`

func buildShutterHelp(locus, addr string) string {
	return fmt.Sprintf(shutterHelpFmt, locus, addr)
}
